package org.oc.qaq.service.Impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.oc.qaq.entity.elasticsearch.DocumentSegment;
import org.oc.qaq.entity.elasticsearch.SegmentRepo;
import org.oc.qaq.entity.FileEntity;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightParameters;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Slf4j
@Service
@RequiredArgsConstructor
public class DocumentService {

    private final ElasticsearchTemplate elasticsearchTemplate;
    private final SegmentRepo segmentRepo;

    public SearchHits<DocumentSegment> search(String keyword) {
        List<HighlightField> fields = List.of(
                new HighlightField("content")
        );
        HighlightParameters parameters = new HighlightParameters.HighlightParametersBuilder()
                .withPreTags("<span style='color:red'>")
                .withPostTags("</span>")
                .build();

        Highlight highlight = new Highlight(parameters, fields);
        HighlightQuery highlightQuery = new HighlightQuery(highlight, DocumentSegment.class);

        NativeQuery query = new NativeQueryBuilder()
                .withQuery(q -> q
                        .multiMatch(
                                m -> m.query(keyword)
                                        .fields("content")
                        ))
                .withHighlightQuery(highlightQuery)
                .build();

        return elasticsearchTemplate.search(query, DocumentSegment.class);
    }

    public void save(FileEntity fileEntity) {
        List<String> pages;
        String id = fileEntity.getId();

        try {
            pages = extractPages(fileEntity.getUrl());

            if (pages != null && !pages.isEmpty()) {
                List<DocumentSegment> segments = new ArrayList<>();
                for (int i = 0; i < pages.size(); i++) {
                    DocumentSegment seg = new DocumentSegment();
                    seg.setDocId(id);
                    seg.setTitle(fileEntity.getName());
                    seg.setPageNum(i + 1);
                    seg.setContent(pages.get(i));
                    segments.add(seg);
                }
                segmentRepo.saveAll(segments);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Async
    public void asyncSave(FileEntity fileEntity) {
        save(fileEntity);
    }


    public void delete(String id) {
        segmentRepo.deleteAllByDocId(id);
    }

    public static List<String> extractPages(String pathOrUrl) throws IOException {
        File file = isUrl(pathOrUrl) ? downloadTempFile(pathOrUrl) : new File(pathOrUrl);
        String extension = getFileExtension(file.getName()).toLowerCase();

        try {
            return switch (extension) {
                case "pdf" -> extractPagesFromPdf(file);
                case "docx" -> extractPagesFromDocx(file);
                default -> throw new UnsupportedOperationException("Unsupported file type: " + extension);
            };
        } finally {
            // 删除临时下载文件
            if (isUrl(pathOrUrl) && file.exists()) {
                file.delete();
            }
        }
    }

    private static List<String> extractPagesFromPdf(File file) throws IOException {
        List<String> pages = new ArrayList<>();
        try (PDDocument document = PDDocument.load(file)) {
            PDFTextStripper stripper = new PDFTextStripper();
            int pageCount = document.getNumberOfPages();

            for (int i = 1; i <= pageCount; i++) {
                stripper.setStartPage(i);
                stripper.setEndPage(i);
                String text = stripper.getText(document).trim();
                if (!text.isEmpty()) {
                    pages.add(text);
                }
            }
        }
        return pages;
    }

    private static List<String> extractPagesFromDocx(File file) throws IOException {
        List<String> pages = new ArrayList<>();
        try (FileInputStream fis = new FileInputStream(file);
             XWPFDocument doc = new XWPFDocument(fis)) {

            List<String> currentPage = new ArrayList<>();
            for (XWPFParagraph paragraph : doc.getParagraphs()) {
                String text = paragraph.getText();
                if (text != null && !text.trim().isEmpty()) {
                    currentPage.add(text.trim());

                    // 模拟分页符号（如果存在）
                    if (text.contains("\f") || text.contains("------ Page Break ------")) {
                        pages.add(String.join("\n\n", currentPage));
                        currentPage.clear();
                    }
                }
            }

            if (!currentPage.isEmpty()) {
                pages.add(String.join("\n\n", currentPage));
            }
        }
        return pages;
    }

    private static boolean isUrl(String path) {
        return path.startsWith("http://") || path.startsWith("https://");
    }

    private static File downloadTempFile(String urlString) throws IOException {
        URL url = new URL(urlString);
        String suffix = getFileExtension(url.getPath());
        File tempFile = File.createTempFile("document_", "." + suffix);
        try (InputStream in = url.openStream()) {
            Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        }
        return tempFile;
    }

    private static String getFileExtension(String fileName) {
        return Optional.ofNullable(fileName)
                .filter(f -> f.contains("."))
                .map(f -> f.substring(fileName.lastIndexOf(".") + 1))
                .orElse("");
    }

    public void deleteByDocId(String id) {
        segmentRepo.deleteAllByDocId(id);
    }
}
